package dom.xpath.query;

import dom.xpath.core.Progress;
import dom.xpath.core.Prop;
import dom.xpath.data.Nodes;
import dom.xpath.data.Result;
import dom.xpath.util.Performance;
import dom.xpath.util.Token;
import dom.xpath.util.TokenBuilder;
import static dom.xpath.core.proc.ProcText.*;

/**
 * This abstract class contains various methods which allow querying in
 * the database. A variety of hierarchic parsers (XPath, XQuery, etc..)
 * can be implemented on top of this class.
 *
 * @author Workgroup DBIS, University of Konstanz 2005-07, ISC License
 * @author Christian Gruen
 */
public abstract class QueryProcessor extends Progress {
  /** Initial node set. */
  public byte[] query;
  /** Expression context. */
  protected QueryContext context;
  /** Compilation time. */
  private long compTime;
  /** Evaluation time. */
  private long evalTime;
  /** Number of calls. */
  private int runs;

  /**
   * Default Constructor.
   * @param q query
   */
  public QueryProcessor(final String q) {
    query = Token.token(q);
  }

  /**
   * Parses and compiles the specified query.
   * @return query expression
   * @throws QueryException query exception
   */
  protected abstract QueryContext parse() throws QueryException;
  
  /**
   * Parses the specified query and writes the result to the
   * specified output stream.
   * @param nodes node context
   * @return query expression
   * @throws QueryException query exception
   */
  public final QueryContext compile(final Nodes nodes) throws QueryException {
    context = parse();
    progress(context);
    context.compile(nodes);
    return context;
  }
  
  /**
   * Parses the specified query and returns the result nodes.
   * @param nodes node context
   * @return result of query
   * @throws QueryException query exception
   */
  public final Nodes queryNodes(final Nodes nodes) throws QueryException {
    final Result res = query(nodes);
    if(res instanceof Nodes) return (Nodes) res;
    throw new EvalException(QUERYNODESERR);
  }

  /**
   * Parses the specified query and returns the result.
   * @param n node context
   * @return result of query
   * @throws QueryException query exception
   */
  public final Result query(final Nodes n) throws QueryException {
    runs++;
    if(!Prop.info) return compile(n).eval(n);

    // add query information 
    final Performance perf = new Performance();
    compile(n);
    compTime += perf.getTime();
    final Result result = context.eval(n);
    evalTime += perf.getTime();
    return result;
  }
  
  /**
   * Returns query background information.
   * @return background information
   */
  public final String getInfo() {
    if(context == null) return "";
    
    final TokenBuilder tb = new TokenBuilder(context.info());
    if(Prop.allInfo) {
      tb.add(NL + QUERYSTRING);
      tb.add(query);
    }
    if(Prop.info) {
      tb.add(NL + QUERYCOMPILE);
      tb.add(Performance.getTimer(compTime, runs));
      tb.add(NL + QUERYEVALUATE);
      tb.add(Performance.getTimer(evalTime, runs));
      //tb.add(NL + QUERYMEM, Performance.getMem());
    }
    return tb.toString();
  }
  
  @Override
  public final String head() {
    return QUERYINFO;
  }

  @Override
  public final String det() {
    return QUERYINFO;
  }

  @Override
  public final double prog() {
    return 0;
  }
}
